/**
*  \file test.h
*
*  \brief   Unit test framework
*
*  \author  Michael J. Hohmann <michael.hohmann@linde-mh.de>
*
*
*/

#ifndef TEST_H_INCLUDED
#define TEST_H_INCLUDED

/*================================================[ public includes  ]================================================*/
#include "../cfg/prj.h"
#include "../cfg/test_cfg.h"
#include "stubbing_tool.h"
#include <setjmp.h>

/*================================================[ public defines   ]================================================*/


/** Report a test result.
*
*  The macro test() calls the function test_case() and add the filename and the line number.
*  \attention Do not call the function test_case() directly.
*
*  \param result Result of the test case.
*  \param description Description of the test case.
*/
#define test( label, description, result   )   test_case( ((void)(test_before_execution()&&test_debug_location()),result), label, #label, description, __FILE__, __LINE__, __func__);

/** Initiate the a test section
*
*  \param id Identification of the test section.
*  \param description Description of the test section.
*/
#define test_section(id, description) for(test_auto_open_section( id, #id, description,__FILE__,__LINE__,__func__);test_auto_close_section();)

/** Initiate a test
*
*  The macro test_initiate() will initialize the test unit and perform the subsequent block once.
*  If the bloc is ended it will close the test.
*
*  \param random_seed   Seed for the internal random number generator.
*  \param mode
*  \param description   Description of the test.
*/
#define test_execute( description, mode, random_seed )                        \
   for(test_auto_open( description, mode, random_seed );test_auto_close();)

/** Test that an assert is raised.
*
*  The macro test_assert() add code to execute the passed function and fetch an assert.
*  If no assert is raised an error will be reported.
*
*  \param func Function to be tested.
*  \param id   Identification of the test case.
*  \param description Description of the test case.
*/
#define test_assert( id, description, func)  setjmp( *test_expect_assert()) ? test_case( TRUE, id, #id, description, __FILE__, __LINE__, __func__):( (void)(test_before_execution()&&test_debug_location()),(void) func, test_case( FALSE, id, #id, description, __FILE__, __LINE__, __func__) )

/** test cell
*
*  This function will call the test function, if test result is fail, then .
*  print the parameter value by test_info_x()
*  exp:
*        test_cell("#1",tilt_target_rpm, 1001,"%S16d");
*   and if tilt_target_rpm is not 1001
*        then print as below:
*        info( description='{\n' )
*        info( description='    Fail_Num:' )
*        info( description='#1' )
*        info( description='\n    ' )
*        info( description='tilt_target_rpm: "1000"' )
*        info( description='\n}\n' )
*   and the pdf will be:
*        {
*           "Fail_Num":"#1",
*           "tilt_target_rpm": "1000"
*        }
*
*/
#define test_cell(function, NUM, paraname, value, type)                 \
{                                                                       \
   CH description[200] = {0};                                            \
   memcpy(description, NUM, sizeof(NUM));                               \
   description[sizeof(NUM)] = ' ';                                      \
   memcpy(description+sizeof(NUM) + 1, #paraname, sizeof(#paraname));   \
   test(0, description, paraname == value);                             \
   if(paraname != value)                                                \
   {                                                                    \
         test_info("{\\n");                                             \
         test_info("    \"Function_Name\": \"");                        \
         test_info(function);                                           \
         test_info("\",\\n");                                           \
         test_info("    \"Fail_Num\": \"");                             \
         test_info(NUM);                                                \
         test_info("\",\\n    \"");                                     \
         memset(description,0,200);                                      \
         memcpy(description, #paraname, sizeof(#paraname));             \
         description[sizeof(#paraname)-1] = '\"';                       \
         description[sizeof(#paraname)] = ':';                          \
         description[sizeof(#paraname)+1] = ' ';                        \
         memcpy(description+sizeof(#paraname)+1, #type, sizeof(#type)); \
         test_info_x(description, &paraname);                           \
         test_info("\\n}\\n");                                          \
   }                                                                    \
}
/*================================================[ public datatypes ]================================================*/
enum test_mode
{
   test_mode_release               = 0x00,
   test_mode_repeat_on_error       = 0x01,
   test_mode_report_no_passed      = 0x02,
   test_mode_report_no_section     = 0x04,
   test_mode_report_no_function    = 0x08,
   test_mode_report_no_timing      = 0x10,
   test_mode_report_no_id          = 0x20,
   test_mode_report_no_count       = 0x40,
   test_mode_report_no_description = 0x80,
   test_mode_develop               = 0xFE,
   test_mode_debug                 = 0xFF
};

/*================================================[ public variables ]================================================*/

/*================================================[ public functions ]================================================*/

/** Report a test case.
*
*  \param start_time Take the time before the function passed as "result" is executed.
*  \param result Result of the test case.
*  \param stop_time Take the time after the function passed as "result" is executed.
*  \param id Identification of the test case.
*  \param label Label of the test case.
*  \param description Description of the test case.
*  \param file Filename where from the test case is executed.
*  \param line Line of code where the test case is executed from.
*  \param function Name of the current function.
*
*  \attention It is not expected that the user is calling this function directly.
*             Use the macro test instead.
*/
void test_case( BO result, enum test_case id, CH const * label, CH const * description, CH const * file, U32 line, CH const * function);

/** Report additional information
*
*  \param description A string which will be reported.
*/
void test_info( CH const * description );

/** Report additional information with argument.
*
*  \param description A string which will be reported. 
*  \param arg argument for types from U8 to S64
*/
void test_info_x( CH const * description, void* arg);

/** Fill an memory object with pseudo random numbers
*
*  \param data Pointer to an memory object.
*  \param size Size of the memory object.
*/
void test_rand( void *data, U32 size );

/** Get U8 random number
*
*  \return Random number
*/
U8 test_rand_u8( void  );

/** Get S8 random number
*
*  \return Random number
*/
S8 test_rand_s8( void  );



/** Get U16 random number
*
*  \return Random number
*/
U16 test_rand_u16( void  );

/** Get S16 random number
*
*  \return Random number
*/
S16 test_rand_s16( void  );

/** Get U32 random number
*
*  \return Random number
*/
U32 test_rand_u32( void  );

/** Get S32 random number
*
*  \return Random number
*/
S32 test_rand_s32( void  );

/** Get pal_size_t random number
*
*  \return Random number
*/
pal_size_t test_ran_size( void );

/** Get boolean random number
*
*  \return Random number
*/
BO test_rand_bo( void );

/**	Catch exceptions.
*
*	This function should be called by the exception handler of the Project to indicate that an exception
*	occurred. This should be in the assert processing, likely in /cfg/assert_cfg.h .
*
* 	\param description Description of the exception.
* 	\param file Filename of the code that causes the exception.
* 	\param line Line of code that causes the exception.
* 	\param function Name of the function that causes the exception.
*/
void test_catch_exception( CH const * description, CH const *file, U32 line, CH const *function );

/** Expect assert
*
*  The function test_expect_assert() is called by the macro test_assert().
*  The function stores the information that an assert is expected and return a buffer which is needed
*  by setjmp to return if an assert is called.
*
*  \attention It is not expected that this function will be called directly.
*
*  /return Pointer to a longjump buffer. This buffer will be used by the test-unit to jump back if an assert is raised.
*/
jmp_buf *test_expect_assert( void );

/** Automatically open a test.
*
*  This function will be used by the macro test_initiate().
*  The function will set internal states that expect that the function test_auto_close() is called twice after.
*
*  \attention It is not expected that this function will be called directly.
*
*  \param description Description of the test.
*  \param mode Test mode.
*  \param random_seed Seed for the internal random number generator.
*/
void test_auto_open(CH const * description, enum test_mode mode, U32 random_seed );

/** Automatically close a test
*
*  This function will be called by the macro test_initiate().
*  It is expected that this function is called twice after one call of test_auto_open().
*
*  \attention It is not expected that this function will be called directly.
*
*/
BO test_auto_close(void);

/** Open a test section
*
*  This function will be used by the macro test_section().
*  The function will set internal states that expect that the function test_auto_close_section() is called twice after.
*
*  \attention It is not expected that this function will be called directly.
*
*  \param id Identification of the test case.
*  \param label Label of the test case.
*  \param description Description of the test section.
*  \param file Filename where from the test case is executed.
*  \param line Line of code where the test case is executed from.
*  \param function Name of the current function.
*/
void test_auto_open_section( enum test_case id, CH const * label, CH const * description, CH const * file, U32 line, CH const * function );

/** Automatically close the test.
*
*  This function will be used by the macro test_section().
*  It is expected that this function is called twice after one call of test_auto_open_section().
*  \attention It is not expected that this function will be called directly.
*/
BO test_auto_close_section(void);

/** Before execution of a test
*
*  This function will be called by the marcros test() and test_execption().
*  It will set set the start time of the test.
*
*  \attention It is not expected that this function will be called directly.
*
*  \return True if the function test_debug_location() should be called.
 */
BO test_before_execution(void);



#endif

/***********************************************************************************************************************
*                             Copyright 2011-1017 Linde Material Handling. All rights reserved.                        *
***********************************************************************************************************************/
